﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Reflection;

namespace BulletOnRails4NET.Models
{
    public class ControlBreakLoop<T_READRECORD, T_PROCESSRECORD, T_CONTROL_KEY_TYPE> where T_CONTROL_KEY_TYPE : IComparable
    {
        public delegate void OnBeginControlBreakDelegate(T_READRECORD record_beginning,
            T_PROCESSRECORD record_processing);
        public delegate void OnEndControlBreakDelegate(T_READRECORD record_ending,
            T_PROCESSRECORD record_processing);
        public delegate void OnDetailDelegate(T_READRECORD current_record,
            T_PROCESSRECORD record_processing);
        public OnBeginControlBreakDelegate OnBeginControlBreak { get; set; }
        public OnEndControlBreakDelegate OnEndControlBreak { get; set; }
        public OnDetailDelegate OnDetail { get; set; }


        IEnumerable<T_READRECORD> _records;
        T_PROCESSRECORD _record_for_process;
        PropertyInfo _control_property;

        public ControlBreakLoop(IEnumerable<T_READRECORD> records, T_PROCESSRECORD
            record_for_process, string control_property_name)
        {
            _records = records;
            _record_for_process = record_for_process;
            _control_property = typeof(T_READRECORD).GetProperty(control_property_name);
        }

                
        /// <summary>
        /// Iterates trhough the records and calls OnBeginControlBreak when a new break
        /// occurs, OnDetail every time a record is read and OnEndControlBreak when
        /// the control break ends due to end of records or end of control value.
        /// </summary>
        public void DoLoop()
        {
            bool first_record = true;
            T_CONTROL_KEY_TYPE last_control_value = default(T_CONTROL_KEY_TYPE), control_curr_value;
            T_READRECORD last_record = default(T_READRECORD);
            foreach (T_READRECORD curr_record in _records)
            {
                control_curr_value = (T_CONTROL_KEY_TYPE) _control_property.GetValue(curr_record, null);
                if (first_record || 
                    (!last_control_value.Equals(control_curr_value))
                    )
                {
                    if (first_record)
                    {
                        first_record = false;
                    }
                    else
                    {
                        OnEndControlBreak(last_record, _record_for_process);
                    }
                    OnBeginControlBreak(curr_record, _record_for_process);
                    last_control_value = control_curr_value;
                }
                OnDetail(curr_record, _record_for_process);
                last_record = curr_record;
            }
            OnEndControlBreak(last_record, _record_for_process);
        }

    }
}
